package com.nowar.common.util;

import java.time.LocalDateTime;
import java.time.ZoneOffset;

/**
 * @Author: 谢无殇。
 * @Date : 2023-02-26 23:38
 */
public class SnowFlakeUtil {

    /**
     * 开始时间截 (这个用自己业务系统上线的时间)
     */
    private static final long TWEPOCH = 1435104000000L;

    /**
     * 机器id所占的位数
     */
    private static final long WORKER_ID_BITS = 10L;

    /**
     * 支持的最大机器id，结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
     */
    private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);

    /**
     * 序列在id中占的位数
     */
    private static final long SEQUENCE_BITS = 12L;

    /**
     * 机器ID向左移12位
     */
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;

    /**
     * 时间截向左移22位(10+12)
     */
    private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;

    /**
     * 生成序列的掩码，这里为4095 (0b111111111111=0xfff=4095)
     */
    private static final long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS);

    /**
     * 工作机器ID(0~1024)
     */
    private static long WORKER_ID =12L;

    /**
     * 毫秒内序列(0~4095)
     */
    private static long SEQUENCE = 0L;

    /**
     * 上次生成ID的时间截
     */
    private static long LAST_TIMESTAMP = -1L;

    //==============================Constructors=====================================

    /**
     * 构造函数
     *
     * @param workerId 工作ID (0~1024)
     */
    public SnowFlakeUtil(long workerId) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", MAX_WORKER_ID));
        }
        this.WORKER_ID = workerId;
    }

    // ==============================Methods==========================================

    /**
     * 获得下一个ID (该方法是线程安全的)
     *
     * @return SnowflakeId
     */
    public static synchronized long nextId() {
        long timestamp = toEpochMilli();

        //如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < LAST_TIMESTAMP) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", LAST_TIMESTAMP - timestamp));
        }

        //如果是同一时间生成的，则进行毫秒内序列
        if (LAST_TIMESTAMP == timestamp) {
            SEQUENCE = (SEQUENCE + 1) & SEQUENCE_MASK;
            //毫秒内序列溢出
            if (SEQUENCE == 0) {
                //阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(LAST_TIMESTAMP);
            }
        }
        //时间戳改变，毫秒内序列重置
        else {
            SEQUENCE = 0L;
        }

        //上次生成ID的时间截
        LAST_TIMESTAMP = timestamp;

        //移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) //
                | (WORKER_ID << WORKER_ID_SHIFT) //
                | SEQUENCE;
    }

    /**
     * 阻塞到下一个毫秒，直到获得新的时间戳
     *
     * @param lastTimestamp 上次生成ID的时间截
     * @return 当前时间戳
     */
    private static long tilNextMillis(long lastTimestamp) {
        long timestamp = toEpochMilli();
        while (timestamp <= lastTimestamp) {
            timestamp = toEpochMilli();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒为单位的当前时间
     *
     * @return 当前时间(毫秒)
     */
    private static long toEpochMilli() {
        return DateUtil.toEpochMilli();
    }

    public static void main(String[] args) {
        LocalDateTime localDateTime = DateUtil.formatToDateTime("2015-06-24 00:00:00", DateUtil.YYYY_MM_DD_HH_MM_SS);
        System.out.println(localDateTime.toInstant(ZoneOffset.UTC).toEpochMilli());
        System.out.println(DateUtil.toEpochMilli());
        System.out.println(SnowFlakeUtil.nextId());
    }

}
